package mySemantic;

import bit.minisys.minicc.parser.ast.*;

import java.util.*;

/**
 * 语义分析
 * 2022/5/7 周永扬
 */
public class MyASTVisitor implements ASTVisitor {

    private Stack<ASTNode> nodeStack = new Stack<>();
    private Stack<SymbolTable> symbolTableStack = new Stack<>();
    private List<String> types = new ArrayList<>();
    private List<String> params = new ArrayList<>();    //参数
    private Symbol tmpSymbol;   //用来临时记录，符号只在合适的位置加入到符号表
    private Queue<Symbol> paramsQueue = new LinkedList<>();  //记录函数参数表，进入函数前需要把参数都加入到其中
    private List<String> expType = new ArrayList<>();  //表达式类型，用来惊醒类型匹配
    private boolean isReturnExist = false;
    private Set<String> labels = new HashSet<>();   //在每个函数内部才生成，然后离开函数初始化
    private Set<String> jumpLabels = new HashSet<>();
    //编译单元节点，其下节点为全局变量的申明或者函数的申明
    @Override
    public void visit(ASTCompilationUnit program) throws Exception {
        nodeStack.push(program);
        SymbolTable golbalSymbols = new SymbolTable(SymbolTable.TYPE.GLOBAL);   //全局声明
        symbolTableStack.push(golbalSymbols);   //压入全局符号表
        for(ASTNode item : program.items)
        {
            if(item instanceof ASTDeclaration)  //变量申明
                visit((ASTDeclaration) item);
            else if(item instanceof  ASTFunctionDefine) //函数定义
            {
                visit((ASTFunctionDefine) item);
            }
        }
        symbolTableStack.pop();
        nodeStack.pop();
    }


    //变量的声明
    @Override
    public void visit(ASTDeclaration declaration) throws Exception {
        nodeStack.push(declaration);
        types.clear();
        for (ASTToken token : declaration.specifiers)
        {
            types.add(token.value);
        }
        for(ASTInitList initList : declaration.initLists)
        {
            visit(initList);
        }

        types.clear();
        nodeStack.pop();
    }

    //如果经过arrayDeclarator那至少是个数组申明，在这里更改tmpSymbol的类型
    @Override
    public void visit(ASTArrayDeclarator arrayDeclarator) throws Exception {
        nodeStack.push(arrayDeclarator);
        visit(arrayDeclarator.declarator);
        ArraySymbol arraySymbol = new ArraySymbol();

        //如果tmpSymbol已经是数组变量了，意味着是多维度的数组变量
        if(tmpSymbol instanceof ArraySymbol)
        {
            arraySymbol = (ArraySymbol) tmpSymbol;
            arraySymbol.dimension++;
        }
        else if(tmpSymbol instanceof VarSymbol)
        {

            arraySymbol.id = tmpSymbol.id;
            arraySymbol.specifiers.addAll(((VarSymbol) tmpSymbol).specifiers);
            arraySymbol.dimension++;
            arraySymbol.sType = Symbol.SymbolType.ARRAY;
        }
        //不论如何，tmpSymbol 至少为ArraySymbol
        tmpSymbol = arraySymbol;
        nodeStack.pop();
    }

    @Override
    public void visit(ASTVariableDeclarator variableDeclarator) throws Exception {
        nodeStack.push(variableDeclarator);
        visit(variableDeclarator.identifier);
        nodeStack.pop();
    }

    @Override
    public void visit(ASTFunctionDeclarator functionDeclarator) throws Exception {
        nodeStack.push(functionDeclarator);
        visit(functionDeclarator.declarator);
        Symbol varSymbol = tmpSymbol;
        FuncSymbol symbol = new FuncSymbol();
        symbol.specifiers.addAll(types);
        symbol.id = varSymbol.id;
        //需要检查之前是从那个节点遍历到的，如果从funcDefine就是Define，如果是declaration就是申明
        for(ASTNode node : nodeStack)
        {
            if(node instanceof  ASTFunctionDefine)
            {
                symbol.funcType = "Define";
                break;
            }
            else if(node instanceof  ASTDeclaration)
            {
                symbol.funcType = "Declaration";
                break;
            }
        }
        //获取参数信息
        for(ASTParamsDeclarator declarator : functionDeclarator.params)
        {
            visit(declarator);
            if(tmpSymbol instanceof VarSymbol)
            {
                VarSymbol sy = (VarSymbol) tmpSymbol;
                paramsQueue.add(sy);    //将参数添加到参数队列
                symbol.params.add(sy.specifiers);  //参数类型中要进行匹配
            }
        }
            tmpSymbol = symbol;
        nodeStack.pop();
    }

    @Override
    public void visit(ASTParamsDeclarator paramsDeclarator) throws Exception {
        nodeStack.push(paramsDeclarator);
        types.clear();
        for(ASTToken token : paramsDeclarator.specfiers)
            types.add(token.value);
        visit(paramsDeclarator.declarator);
        tmpSymbol.specifiers.addAll(types);
        params.clear();
        params.addAll(types);
        types.clear();
        nodeStack.pop();
    }

    @Override
    public void visit(ASTArrayAccess arrayAccess) throws Exception {
        nodeStack.push(arrayAccess);
        visit(arrayAccess.arrayName);
        String arrayName = tmpSymbol.id;
        for(ASTExpression expression : arrayAccess.elements)
        {
            visit(expression);
            if(!expType.get(0).equals("int"))
            {
                ErrorPrint.arrayAccessNotInteger(arrayName);
            }
        }
        nodeStack.pop();
    }

    @Override
    public void visit(ASTBinaryExpression binaryExpression) throws Exception {
        nodeStack.push(binaryExpression);
        visit(binaryExpression.expr1);
        List<String> expType1 = new ArrayList<>();
        expType1.addAll(expType);
        visit(binaryExpression.expr2);
        List<String> expType2 = new ArrayList<>();
        expType2.addAll(expType);
        String op = binaryExpression.op.value;
        expType = matchExpType(expType1, expType2, op);
        if(expType == null) {
            ErrorPrint.operateNumNotMatched(op);
            expType = new ArrayList<>();
            expType.add("int");
        }
        nodeStack.pop();
    }

    @Override
    public void visit(ASTBreakStatement breakStat) throws Exception {
        nodeStack.push(breakStat);
        boolean isBreakInLoop = false;
        for(int i = nodeStack.size() - 1; i >= 0 ;i--)
        {
            if(nodeStack.get(i) instanceof ASTIterationStatement || nodeStack.get(i) instanceof ASTIterationDeclaredStatement)
            {
                isBreakInLoop = true;
                break;
            }
        }
        if(!isBreakInLoop)
            ErrorPrint.breakNotInLoopStat();
        nodeStack.pop();
    }

    @Override
    public void visit(ASTContinueStatement continueStatement) throws Exception {

    }

    @Override
    public void visit(ASTCastExpression castExpression) throws Exception {

    }

    @Override
    public void visit(ASTCharConstant charConst) throws Exception {
        nodeStack.push(charConst);
        expType.clear();
        expType.add("char");
        nodeStack.pop();
    }

    @Override
    public void visit(ASTCompoundStatement compoundStat) throws Exception {
        nodeStack.push(compoundStat);
        //添加参数符号
        SymbolTable symbolTable = new SymbolTable(SymbolTable.TYPE.LOCAL);
        //添加局部变量
        int size = paramsQueue.size();
        for(int i = 0; i < size; i++)
            symbolTable.add(paramsQueue.poll());
        //进入符合语句，添加一级表
        symbolTableStack.push(symbolTable);
        for (ASTNode node : compoundStat.blockItems) {
            if(node instanceof ASTDeclaration) {
                visit((ASTDeclaration)node);
            }else if (node instanceof ASTStatement) {
                visit((ASTStatement)node);
            }
        }
        symbolTableStack.pop();
        nodeStack.pop();
    }

    @Override
    public void visit(ASTConditionExpression conditionExpression) throws Exception {

    }

    @Override
    public void visit(ASTExpression expression) throws Exception {
        if(expression instanceof ASTArrayAccess) {
            visit((ASTArrayAccess)expression);
        }else if(expression instanceof ASTBinaryExpression) {
            visit((ASTBinaryExpression)expression);
        }else if(expression instanceof ASTCastExpression) {
            visit((ASTCastExpression)expression);
        }else if(expression instanceof ASTCharConstant) {
            visit((ASTCharConstant)expression);
        }else if(expression instanceof ASTConditionExpression) {
            visit((ASTConditionExpression)expression);
        }else if(expression instanceof ASTFloatConstant) {
            visit((ASTFloatConstant)expression);
        }else if(expression instanceof ASTFunctionCall) {
            visit((ASTFunctionCall)expression);
        }else if(expression instanceof ASTIdentifier) {
            visit((ASTIdentifier)expression);
        }else if(expression instanceof ASTIntegerConstant) {
            visit((ASTIntegerConstant)expression);
        }else if(expression instanceof ASTMemberAccess) {
            visit((ASTMemberAccess)expression);
        }else if(expression instanceof ASTPostfixExpression) {
            visit((ASTPostfixExpression)expression);
        }else if(expression instanceof ASTStringConstant) {
            visit((ASTStringConstant)expression);
        }else if(expression instanceof ASTUnaryExpression) {
            visit((ASTUnaryExpression)expression);
        }else if(expression instanceof ASTUnaryTypename){
            visit((ASTUnaryTypename)expression);
        }
    }

    @Override
    public void visit(ASTExpressionStatement expressionStat) throws Exception {
        for(ASTExpression expression : expressionStat.exprs)
        {
            visit(expression);
        }
    }

    @Override
    public void visit(ASTFloatConstant floatConst) throws Exception {
        nodeStack.push(floatConst);
        expType.clear();
        expType.add("double");
        nodeStack.pop();
    }

    @Override
    public void visit(ASTFunctionCall funcCall) throws Exception {
        nodeStack.add(funcCall);
        visit(funcCall.funcname);
        List<String> retType = expType; //保存函数返回值
        String funcName = tmpSymbol.id;

        if(isSymbolExist(funcName))
        {
            VarSymbol symbol = (VarSymbol) getSymbol(funcName);
            if(symbol instanceof FuncSymbol) {
                FuncSymbol funcSymbol = (FuncSymbol) symbol;
                boolean isSame = paramsCheck(funcCall.argList, funcSymbol);
                if (!isSame)
                    ErrorPrint.paramsNotMatched(funcName, "type");
            }
            else
            {
                ErrorPrint.varNotDefined(funcName, "Function"); //函数未定义就调用
            }
        }
        expType = retType;
        nodeStack.pop();
    }

    @Override
    public void visit(ASTGotoStatement gotoStat) throws Exception {
        nodeStack.push(gotoStat);
        visit(gotoStat.label);
        jumpLabels.add(tmpSymbol.id);
        nodeStack.pop();
    }

    @Override
    public void visit(ASTIdentifier identifier) throws Exception {
        nodeStack.push(identifier);
        boolean isDeclarator = false;
        tmpSymbol = new VarSymbol();
        tmpSymbol.sType = Symbol.SymbolType.VAR;    //首先默认为变量型
        tmpSymbol.id = identifier.value;            //标识符一致
        //首先检查是否是从declarator 或者 labeledStatement遍历到的，如果不是，那出现的标识符就是没有申明的
        for(int i = nodeStack.size() - 1; i >= 0; i--)
        {
            ASTNode node = nodeStack.get(i);
            if(node instanceof ASTVariableDeclarator || node instanceof ASTLabeledStatement)
            {
                isDeclarator = true;
                break;
            }
        }
        //不是声明的情况，如果没有出现过就是错误的，如果出现过就不用反应
        if(!isDeclarator)
        {
            if(!isSymbolExist(tmpSymbol.id))
            {
                boolean isFuncName = false;
                for(int i = nodeStack.size() - 1; i >= 0; i--)
                {
                    if(nodeStack.get(i) instanceof ASTFunctionCall)
                    {
                        if(((ASTFunctionCall) nodeStack.get(i)).funcname instanceof ASTIdentifier)
                        {
                            ASTFunctionCall functionCall = (ASTFunctionCall) nodeStack.get(i);
                            ASTIdentifier id = (ASTIdentifier) functionCall.funcname;
                             isFuncName =  id.value.equals(tmpSymbol.id);
                        }
                        break;
                    }
                    if(nodeStack.get(i) instanceof ASTGotoStatement)
                    {
                        ((ASTGotoStatement) nodeStack.get(i)).label.value.equals(tmpSymbol.id);
                        expType = new ArrayList<>();
                        return;
                    }
                }
                if(!isFuncName)
                    ErrorPrint.varNotDefined(tmpSymbol.id, "Identifier");
                else
                    ErrorPrint.varNotDefined(tmpSymbol.id, "Function");
                expType = new ArrayList<>();
            }
            //设置表达式类型
            else {
                expType = findIdentifierExpType(tmpSymbol.id);
            }
        }
        nodeStack.pop();
    }


    //变量申明及初始化单元
    @Override
    public void visit(ASTInitList initList) throws Exception {
        nodeStack.push(initList);
        visit(initList.declarator);
        //到了initList 可以添加到符号表了，只有数组和变量再此添加
        VarSymbol varSymbol = (VarSymbol) tmpSymbol;
        varSymbol.specifiers.addAll(types);
        if(!isSymbolExistInLocal(varSymbol.id))
            getLastSymbolTable().add(varSymbol);
        else
            ErrorPrint.varDefinedAgain(varSymbol.id, "Identifier");
        for(ASTExpression expression : initList.exprs)
        {
            visit(expression);
        }
        nodeStack.pop();
    }

    @Override
    public void visit(ASTIntegerConstant intConst) throws Exception {
        nodeStack.push(intConst);
        expType.clear();
        expType.add("int");
        nodeStack.pop();
    }

    @Override
    public void visit(ASTIterationDeclaredStatement iterationDeclaredStat) throws Exception {
        nodeStack.push(iterationDeclaredStat);
        symbolTableStack.push(new SymbolTable(SymbolTable.TYPE.LOOPTEMP));
        visit(iterationDeclaredStat.init);
        for(ASTExpression expression : iterationDeclaredStat.cond)
        {
            visit(expression);
        }
        for(ASTExpression expression : iterationDeclaredStat.step)
        {
            visit(expression);
        }
        visit(iterationDeclaredStat.stat);
        symbolTableStack.pop();
        nodeStack.pop();
    }

    @Override
    public void visit(ASTIterationStatement iterationStat) throws Exception {
        nodeStack.push(iterationStat);
        for(ASTExpression expression : iterationStat.cond)
        {
            visit(expression);
        }
        for(ASTExpression expression : iterationStat.step)
        {
            visit(expression);
        }
        visit(iterationStat.stat);
        nodeStack.pop();
    }

    @Override
    public void visit(ASTLabeledStatement labeledStat) throws Exception {
        nodeStack.push(labeledStat);
        visit(labeledStat.label);
        labels.add(tmpSymbol.id);
        visit(labeledStat.stat);
        nodeStack.pop();
    }

    @Override
    public void visit(ASTMemberAccess memberAccess) throws Exception {
        nodeStack.push(memberAccess);
        //To do part
        nodeStack.pop();
    }

    @Override
    public void visit(ASTPostfixExpression postfixExpression) throws Exception {
        nodeStack.push(postfixExpression);
        //To do part
        nodeStack.pop();
    }

    @Override
    public void visit(ASTReturnStatement returnStat) throws Exception {
        nodeStack.push(returnStat);
        for(int i = nodeStack.size() - 1; i >= 0; i--)
        {
            if(nodeStack.get(i) instanceof ASTFunctionDefine)
            {
                ASTFunctionDefine functionDefine = (ASTFunctionDefine) nodeStack.get(i);
                isReturnExist = true;
                for(ASTExpression exp : returnStat.expr)
                {
                    visit(exp);
                }
                if(!expType.get(0).equals(functionDefine.specifiers.get(0).value))
                {
                    ErrorPrint.returnTypeNotMatchFuncDefine(functionDefine.declarator.getName());
                }
            }
        }
        nodeStack.pop();
    }

    @Override
    public void visit(ASTSelectionStatement selectionStat) throws Exception {
        nodeStack.push(selectionStat);
        for(ASTExpression expression : selectionStat.cond)
        {
            visit(expression);
        }
        visit(selectionStat.then);
        visit(selectionStat.otherwise);
        nodeStack.pop();
    }

    @Override
    public void visit(ASTStringConstant stringConst) throws Exception {
        nodeStack.push(stringConst);
        expType.clear();
        expType.add("StringConstant");
        nodeStack.pop();
    }

    @Override
    public void visit(ASTTypename typename) throws Exception {

    }

    @Override
    public void visit(ASTUnaryExpression unaryExpression) throws Exception {

    }

    @Override
    public void visit(ASTUnaryTypename unaryTypename) throws Exception {

    }

    @Override
    public void visit(ASTFunctionDefine functionDefine) throws Exception {
        nodeStack.push(functionDefine);
        isReturnExist = false;
        types.clear();
        String funcName = "";
        paramsQueue.clear();
        for(ASTToken token : functionDefine.specifiers)
        {
            types.add(token.value);
        }
        visit(functionDefine.declarator);
        //获取完了函数的所有信息
        if(!isSymbolExist(tmpSymbol.id)) {
            getLastSymbolTable().add(tmpSymbol);   //添加函数符号
            funcName = tmpSymbol.id;
        }
        else {
            FuncSymbol funcSymbol = (FuncSymbol) getSymbol(tmpSymbol.id);
            if(funcSymbol.funcType.equals("Define")) {
                ErrorPrint.varDefinedAgain(tmpSymbol.id, "Function");
            }
            else
            {
                getLastSymbolTable().symbolList.remove(funcSymbol);
                funcSymbol.funcType = "Define";
                getLastSymbolTable().add(funcSymbol);
            }
            funcName = funcSymbol.id;
        }
        types.clear();
        visit(functionDefine.body);
        if(!isReturnExist)
            ErrorPrint.funcNotEndWithReturn(funcName);

        for(String label : jumpLabels)
        {
            if(!labels.contains(label))
            {
                ErrorPrint.gotoLableNotExist(label);
            }
        }

        labels.clear();
        jumpLabels.clear();
        nodeStack.pop();
    }

    @Override
    public void visit(ASTDeclarator declarator) throws Exception {
        nodeStack.push(declarator);
        if(declarator instanceof ASTVariableDeclarator)
        {
            visit((ASTVariableDeclarator)declarator);
        }
        else if(declarator instanceof  ASTArrayDeclarator)
        {
            visit((ASTArrayDeclarator) declarator);
        }
        else if(declarator instanceof ASTFunctionDeclarator)
        {
            visit((ASTFunctionDeclarator) declarator);
        }
        nodeStack.pop();
    }

    @Override
    public void visit(ASTStatement statement) throws Exception {
        nodeStack.push(statement);
        if(statement instanceof ASTIterationDeclaredStatement) {
            visit((ASTIterationDeclaredStatement)statement);
        }else if(statement instanceof ASTIterationStatement) {
            visit((ASTIterationStatement)statement);
        }else if(statement instanceof ASTCompoundStatement) {
            visit((ASTCompoundStatement)statement);
        }else if(statement instanceof ASTSelectionStatement) {
            visit((ASTSelectionStatement)statement);
        }else if(statement instanceof ASTExpressionStatement) {
            visit((ASTExpressionStatement)statement);
        }else if(statement instanceof ASTBreakStatement) {
            visit((ASTBreakStatement)statement);
        }else if(statement instanceof ASTContinueStatement) {
            visit((ASTContinueStatement)statement);
        }else if(statement instanceof ASTReturnStatement) {
            visit((ASTReturnStatement)statement);
        }else if(statement instanceof ASTGotoStatement) {
            visit((ASTGotoStatement)statement);
        }else if(statement instanceof ASTLabeledStatement) {
            visit((ASTLabeledStatement)statement);
        }
        nodeStack.pop();
    }

    @Override
    public void visit(ASTToken token) throws Exception {

    }

    private boolean isSymbolExist(String id)
    {
        int i = symbolTableStack.size() - 1;
        while(i >= 0)
        {
            SymbolTable table = symbolTableStack.get(i);
            Symbol symbol = table.getSymbol(id);
            if(symbol != null)
                return true;
            i--;
        }
        return false;
    }

    private boolean isSymbolExistInLocal(String id)
    {
        return symbolTableStack.peek().getSymbol(id) != null;
    }

    private Symbol getSymbol(String id)
    {
        //获取的符号是最近的，也就是优先局部，延后全局的获取逻辑
        int i = symbolTableStack.size() - 1;
        while(i >= 0)
        {
            SymbolTable table = symbolTableStack.get(i);
            Symbol symbol = table.getSymbol(id);
            if(symbol != null)
                return symbol;
            i--;
        }
        return null;
    }


    private SymbolTable getLastSymbolTable()
    {
        if(symbolTableStack.size() > 0)
            return symbolTableStack.peek();
        return null;
    }

    private List<String> matchExpType(List<String> srcTypes, List<String> desTypes, String op)
    {

        String type1 = "";
        String type2 = "";
        if(srcTypes.size() > 0)
            type1 = srcTypes.get(0);
        else if(desTypes.size() > 0)
            type1 = desTypes.get(0);
        else
            type1 = "null";
        if (desTypes.size() > 0)
            type2 = desTypes.get(0);
        else if(srcTypes.size() > 0)
            type2 = srcTypes.get(0);
        else
            type2 = "null";

        if(type1.equals("null") || type2.equals("null"))
        {
            ErrorPrint.operateNumNotMatched(op);
            return null;
        }
        List<String> retType = new ArrayList<>();

        switch (op)
        {
            case "=":
            case "+=":
            case "-=":
            case "/=":
            case "*=":
                if (!numTypeOK(type1, type2)) {
                    ErrorPrint.operateNumNotMatched(op);
                }
                retType.add(type1);
                break;
            case "+":
            case "-":
            case "*":
            case "/":
                if (!numTypeOK(type1, type2)) {
                    ErrorPrint.operateNumNotMatched(op);
                }
                retType = higherExpresstionType(type1, type2);
                break;
            case ">":
            case "<":
            case ">=":
            case "<=":
                if (!numTypeOK(type1, type2)) {
                    ErrorPrint.operateNumNotMatched(op);
                }
                retType.add("int");
                break;
            case "%":
            case "%=":
            case "<<":
            case ">>":
            case "<<=":
            case ">>=":
                if (!type1.equals("int") || !type2.equals("int")) {
                    ErrorPrint.operateNumNotMatched(op);
                }
                retType.add("int");
                break;
            case "||":
            case "&&":
                retType.add("int");
                break;
            default:
                System.out.println("Operator not defined");
                retType.add("int");
                break;
        }
        return retType;
    }

    private boolean numTypeOK(String type1 , String type2)
    {
        return (type1.equals("char") || type1.equals("int") || type1.equals("float") || type1.equals("double")) || (type2.equals("int") || type2.equals("char") || type2.equals("float") || type2.equals("double"));
    }

    private List<String> higherExpresstionType(String type1, String type2)
    {
        List<String> retType = new ArrayList<>();
        if(type1.equals("double") || type2.equals("double"))
            retType.add("double");
        else if(type1.equals("float") || type2.equals("float"))
            retType.add("float");
        else
            retType.add("int");
        return retType;
    }

    private boolean paramsCheck(List<ASTExpression> actParams, FuncSymbol funcSymbol) throws Exception {
        if (actParams.size() != funcSymbol.params.size()) //参数数量不匹配
        {
            ErrorPrint.paramsNotMatched(funcSymbol.id, "number");
            return false;
        }
        else
        {
            List<String> paramsType = funcSymbol.specifiers;
            for(int i = 0; i < actParams.size() && i < paramsType.size(); i++)
            {
                visit(actParams.get(i));
                String type1 = expType.get(0);
                String type2 = paramsType.get(i);
                if(!type1.equals(type2))
                    return false;
            }
        }
        return true;
    }

    private List<String> findIdentifierExpType(String id)
    {
        List<String> expTypes = new ArrayList<>();
        Symbol symbol = getSymbol(id);
        expTypes.addAll(symbol.specifiers);
        return expTypes;
    }
}
